/*
 * SPDX-License-Identifier: Apache-2.0
 */

package org.hyperledger.fabric.samples.fabFileModel;

import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import java.text.SimpleDateFormat;
import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.Contact;
import org.hyperledger.fabric.contract.annotation.Contract;
import org.hyperledger.fabric.contract.annotation.Default;
import org.hyperledger.fabric.contract.annotation.Info;
import org.hyperledger.fabric.contract.annotation.License;
import org.hyperledger.fabric.contract.annotation.Transaction;
import org.hyperledger.fabric.shim.ChaincodeException;
import org.hyperledger.fabric.shim.ChaincodeStub;
import org.hyperledger.fabric.shim.ledger.KeyValue;
import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;

import com.owlike.genson.Genson;





/**
 * Java implementation of the Fabric FileModel Contract described in the Writing Your
 * First Application tutorial
 */
@Contract(
        name = "FabFileModel",
        info = @Info(
                title = "FabFileModel contract",
                description = "The hyperlegendary fileModel contract",
                version = "0.0.1-SNAPSHOT",
                license = @License(
                        name = "Apache 2.0 License",
                        url = "http://www.apache.org/licenses/LICENSE-2.0.html"),
                contact = @Contact(
                        email = "f.fileModelr@example.com",
                        name = "F Carr",
                        url = "https://hyperledger.example.com")))
@Default
public final class FabFileModel implements ContractInterface {

    private final Genson genson = new Genson();

    private enum FabFileModelErrors {
        CAR_NOT_FOUND,
        CAR_ALREADY_EXISTS
    }

    /**
     * Retrieves a fileModel with the specified key from the ledger.
     *
     * @param ctx the transaction context
     * @param key the key
     * @return the FileModel found on the ledger if there was one
     */
    @Transaction()
    public FileModel queryFileModel(final Context ctx, final String key) {
        ChaincodeStub stub = ctx.getStub();
        String fileModelState = stub.getStringState(key);

        if (fileModelState.isEmpty()) {
            String errorMessage = String.format("FileModel %s does not exist", key);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage, FabFileModelErrors.CAR_NOT_FOUND.toString());
        }

        FileModel fileModel = genson.deserialize(fileModelState, FileModel.class);

        return fileModel;
    }  

    /**
     * Creates some initial Cars on the ledger.
     *
     * @param ctx the transaction context
     */
    @Transaction()
    public void initLedger(final Context ctx) {
        ChaincodeStub stub = ctx.getStub();

        String[] fileModelData = {
                "{ \"filePath\": \"Toyota\", \"fileMd5\": \"Prius\", \"owner\": \"Tomoko\" }"
        };

        for (int i = 0; i < fileModelData.length; i++) {
            String key = String.format("File%d", i);

            FileModel fileModel = genson.deserialize(fileModelData[i], FileModel.class);
            String fileModelState = genson.serialize(fileModel);
            stub.putStringState(key, fileModelState);
        }
    }

    /**
     * Creates a new fileModel on the ledger.
     *
     * @param ctx the transaction context
     * @param key the key for the new fileModel
     * @param filePath the filePath of the new fileModel
     * @param fildMd5 the fildMd5 of the new fileModel
     * @param owner the owner of the new fileModel
     * @param fileName the fileName of the new fileModel
     * @return the created FileModel
     */
    @Transaction()
    public FileModel createFileModel(final Context ctx, final String key, final String filePath,
                                final String fileMd5, final String owner, final String fileName, 
                                final String accessID) {
        ChaincodeStub stub = ctx.getStub();

        String fileModelState = stub.getStringState(key);
        if (!fileModelState.isEmpty()) {
            String errorMessage = String.format("FileModel %s already exists", key);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage, FabFileModelErrors.CAR_ALREADY_EXISTS.toString());
        }
        FileModel fileModel = new FileModel(filePath, fileMd5, owner, fileName, accessID);
        Date now=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd_hh:mm:ss");
        String dateStr=sdf.format(now);
        Log log =new Log(dateStr,accessID,"upload","success");
        String logKey= "log"+dateStr;
        fileModelState = genson.serialize(fileModel);
        stub.putStringState(key, fileModelState);
        String logState = genson.serialize(log);
        stub.putStringState(logKey, logState);
        return fileModel;
    }

    /**
     * Retrieves all fileModels from the ledger.
     *
     * @param ctx the transaction context
     * @return array of Cars found on the ledger
     */
    @Transaction()
    public String queryAllFiles(final Context ctx) {
        ChaincodeStub stub = ctx.getStub();

        final String startKey = "File1";
        final String endKey = "File99";
        List<FileModelQueryResult> queryResults = new ArrayList<FileModelQueryResult>();

        QueryResultsIterator<KeyValue> results = stub.getStateByRange(startKey, endKey);

        for (KeyValue result: results) {
            FileModel fileModel = genson.deserialize(result.getStringValue(), FileModel.class);
            queryResults.add(new FileModelQueryResult(result.getKey(), fileModel));
        }

        final String response = genson.serialize(queryResults);

        return response;
    }

    /**
     * Changes the owner of a fileModel on the ledger.
     *
     * @param ctx the transaction context
     * @param key the key
     * @param newOwner the new owner
     * @return the updated FileModel
     */
    @Transaction()
    public FileModel changeFileOwner(final Context ctx, final String key, final String newOwner) {
        ChaincodeStub stub = ctx.getStub();

        String fileModelState = stub.getStringState(key);

        if (fileModelState.isEmpty()) {
            String errorMessage = String.format("FileModel %s does not exist", key);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage, FabFileModelErrors.CAR_NOT_FOUND.toString());
        }

        FileModel fileModel = genson.deserialize(fileModelState, FileModel.class);

        FileModel newCar = new FileModel(fileModel.getFilePath(), fileModel.getFileMd5(), newOwner, fileModel.getFileName(), fileModel.getAccessID());
        String newCarState = genson.serialize(newCar);
        stub.putStringState(key, newCarState);

        return newCar;
    }
}
